#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DPA (Differential Power Analysis) 差分Power AnalysisAttack
"""

import numpy as np
import matplotlib.pyplot as plt
import time
from typing import Dict, Any
try:
    from .base_side_channel_attack import StatisticalAttack, AttackResult
except ImportError:
    from base_side_channel_attack import StatisticalAttack, AttackResult


class DPAAttack(StatisticalAttack):
    """差分Power AnalysisAttack"""
    
    def __init__(self):
        super().__init__("DPA")
        self.threshold = 0.5  # Default阈值
        self.hypothesis_range = 256  # AddFalse设范围
        
    def set_threshold(self, threshold: float):
        """SettingsDPA阈值"""
        self.threshold = threshold
    
    def attack(self, target_byte: int = 0, threshold: float = None, **kwargs) -> AttackResult:
        """
        ExecuteDPAAttack
        
        Args:
            target_byte: 目标字节位置
            threshold: DPA阈值
            **kwargs: 其他Parameters
            
        Returns:
            AttackResult
        """
        start_time = time.time()
        
        if threshold is not None:
            self.threshold = threshold
            
        if not self.validate_data():
            return AttackResult(
                attack_type="DPA",
                target_byte=target_byte,
                execution_time=0,
                success=False,
                confidence=0.0
            )
        
        try:
            # 获取目标Data
            target_data = self.get_target_data(target_byte)
            
            # CalculateAllFalse设的差分Trace
            differential_traces = np.zeros((self.hypothesis_range, self.traces.shape[1]))
            t_statistics = np.zeros((self.hypothesis_range, self.traces.shape[1]))
            
            for hypothesis in range(self.hypothesis_range):
                # CalculateIntermediate value
                intermediate_values = self.calculate_intermediate_values(target_data, hypothesis)
                
                # According to阈值分组
                group1_indices, group2_indices = self._partition_by_threshold(intermediate_values)
                
                if len(group1_indices) == 0 or len(group2_indices) == 0:
                    continue
                
                # Calculate差分Trace
                group1_traces = self.traces[group1_indices]
                group2_traces = self.traces[group2_indices]
                
                mean1 = np.mean(group1_traces, axis=0)
                mean2 = np.mean(group2_traces, axis=0)
                differential_traces[hypothesis] = mean1 - mean2
                
                # Calculatet统计量
                t_stats, _ = self.perform_t_test(group1_traces, group2_traces)
                t_statistics[hypothesis] = t_stats
            
            # 找To最大差分
            max_diff_indices = np.unravel_index(
                np.argmax(np.abs(differential_traces)), differential_traces.shape
            )
            best_hypothesis = max_diff_indices[0]
            best_sample_point = max_diff_indices[1]
            max_differential = differential_traces[best_hypothesis, best_sample_point]
            
            # CalculateEachFalse设的最大差分（跨AllSampling点）
            max_diff_per_hypothesis = np.max(np.abs(differential_traces), axis=1)
            
            # 获取前 n 个候选
            top_n = kwargs.get('top_n', min(256, self.hypothesis_range))
            sorted_indices = np.argsort(max_diff_per_hypothesis)[::-1]  # 降序排列
            top_candidates = [
                (int(idx), float(max_diff_per_hypothesis[idx])) 
                for idx in sorted_indices[:top_n]
            ]
            
            execution_time = time.time() - start_time
            
            # Calculate置信度
            confidence = abs(max_differential) / np.std(self.traces)
            success = confidence > 0.1  # 阈值可调
            
            self.logger.info(f"DPA Attack completed:")
            self.logger.info(f"  - Best hypothesis: 0x{best_hypothesis:02x}")
            self.logger.info(f"  - Max differential: {max_differential:.4f}")
            self.logger.info(f"  - Sample point: {best_sample_point}")
            self.logger.info(f"  - Confidence: {confidence:.4f}")
            self.logger.info(f"  - Top candidates saved: {len(top_candidates)}")
            
            return AttackResult(
                attack_type="DPA",
                target_byte=target_byte,
                execution_time=execution_time,
                success=success,
                confidence=confidence,
                best_hypothesis=best_hypothesis,
                top_candidates=top_candidates,
                metadata={
                    'differential_traces': differential_traces,
                    't_statistics': t_statistics,
                    'max_differential': max_differential,
                    'best_sample_point': best_sample_point,
                    'threshold': self.threshold,
                    'confidence_threshold': 0.1,
                    'max_diff_per_hypothesis': max_diff_per_hypothesis
                }
            )
            
        except Exception as e:
            self.logger.error(f"DPA attack failed: {e}")
            return AttackResult(
                attack_type="DPA",
                target_byte=target_byte,
                execution_time=time.time() - start_time,
                success=False,
                confidence=0.0
            )
    
    def _partition_by_threshold(self, intermediate_values: np.ndarray):
        """According to阈值分组"""
        # Use汉明权重进行分组
        hamming_weights = np.array([bin(val).count('1') for val in intermediate_values])
        
        # According to阈值分组
        threshold_value = np.median(hamming_weights)
        group1_indices = np.where(hamming_weights >= threshold_value)[0]
        group2_indices = np.where(hamming_weights < threshold_value)[0]
        
        return group1_indices, group2_indices
    
    def plot_results(self, result: AttackResult) -> None:
        """绘制DPAAttackResult"""
        if not result.success or 'differential_traces' not in result.metadata:
            self.logger.warning("No valid results to plot")
            return
        
        differential_traces = result.metadata['differential_traces']
        t_statistics = result.metadata['t_statistics']
        
        fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 10))
        
        # 绘制差分Trace热图
        im1 = ax1.imshow(np.abs(differential_traces), aspect='auto', cmap='hot', interpolation='nearest')
        ax1.set_title('DPA Attack - Differential Traces Heatmap')
        ax1.set_xlabel('Sample Points')
        ax1.set_ylabel('Key Hypothesis')
        plt.colorbar(im1, ax=ax1, label='|Differential|')
        
        # 绘制t统计量热图
        im2 = ax2.imshow(np.abs(t_statistics), aspect='auto', cmap='viridis', interpolation='nearest')
        ax2.set_title('DPA Attack - T-Statistics Heatmap')
        ax2.set_xlabel('Sample Points')
        ax2.set_ylabel('Key Hypothesis')
        plt.colorbar(im2, ax=ax2, label='|T-Statistic|')
        
        # 绘制最佳False设的差分Trace
        best_hypothesis = result.best_hypothesis
        ax3.plot(differential_traces[best_hypothesis, :], 'b-', linewidth=2)
        ax3.set_title(f'Best Hypothesis (0x{best_hypothesis:02x}) Differential Trace')
        ax3.set_xlabel('Sample Points')
        ax3.set_ylabel('Differential')
        ax3.grid(True, alpha=0.3)
        
        # 标记最大差分点
        best_sample_point = result.metadata['best_sample_point']
        max_differential = result.metadata['max_differential']
        ax3.plot(best_sample_point, max_differential, 'ro', markersize=8, 
                label=f'Max: {max_differential:.4f}')
        ax3.legend()
        
        # 绘制AllFalse设的最大差分
        max_differentials_per_hypothesis = np.max(np.abs(differential_traces), axis=1)
        ax4.bar(range(self.hypothesis_range), max_differentials_per_hypothesis, 
               color='lightcoral', alpha=0.7)
        ax4.bar(best_hypothesis, max_differentials_per_hypothesis[best_hypothesis], 
               color='red', alpha=0.8, label=f'Best: 0x{best_hypothesis:02x}')
        ax4.set_title('Maximum Differential per Key Hypothesis')
        ax4.set_xlabel('Key Hypothesis')
        ax4.set_ylabel('Max |Differential|')
        ax4.legend()
        ax4.grid(True, alpha=0.3)
        
        plt.tight_layout()
        plt.show()
